using System;
using System.Security.Cryptography;
using System.Text;

namespace Com.FirstSolver.Splash;

public static class Utils
{
	public static uint ROTL(uint x, int n)
	{
		return (x << n) | (x >> 32 - n);
	}

	public static byte[] GenerateRandom(int keySizeInBytes)
	{
		byte[] Key = new byte[keySizeInBytes];
		using (var Provider = RandomNumberGenerator.Create())
		{
			Provider.GetBytes(Key);
		}
		return Key;
	}

	public static void UInt32ToByteArray(byte[] destination, int destinationIndex, uint[] source, int sourceIndex, int count, ByteOrder order = ByteOrder.LITTLE_ENDIAN)
	{
		if (order.Equals(ByteOrder.LITTLE_ENDIAN))
		{
			for (int j = 0; j < count; j++)
			{
				uint k = source[sourceIndex++];
				destination[destinationIndex++] = (byte)(k & 0xFFu);
				destination[destinationIndex++] = (byte)((k >> 8) & 0xFFu);
				destination[destinationIndex++] = (byte)((k >> 16) & 0xFFu);
				destination[destinationIndex++] = (byte)((k >> 24) & 0xFFu);
			}
		}
		else
		{
			for (int i = 0; i < count; i++)
			{
				uint l = source[sourceIndex++];
				destination[destinationIndex++] = (byte)((l >> 24) & 0xFFu);
				destination[destinationIndex++] = (byte)((l >> 16) & 0xFFu);
				destination[destinationIndex++] = (byte)((l >> 8) & 0xFFu);
				destination[destinationIndex++] = (byte)(l & 0xFFu);
			}
		}
	}

	public static void UInt32ToByteArray(byte[] destination, int destinationIndex, uint source, ByteOrder order = ByteOrder.LITTLE_ENDIAN)
	{
		if (order.Equals(ByteOrder.LITTLE_ENDIAN))
		{
			destination[destinationIndex++] = (byte)(source & 0xFFu);
			destination[destinationIndex++] = (byte)((source >> 8) & 0xFFu);
			destination[destinationIndex++] = (byte)((source >> 16) & 0xFFu);
			destination[destinationIndex++] = (byte)((source >> 24) & 0xFFu);
		}
		else
		{
			destination[destinationIndex++] = (byte)((source >> 24) & 0xFFu);
			destination[destinationIndex++] = (byte)((source >> 16) & 0xFFu);
			destination[destinationIndex++] = (byte)((source >> 8) & 0xFFu);
			destination[destinationIndex++] = (byte)(source & 0xFFu);
		}
	}

	public static byte[] GetBytes(int value, ByteOrder order = ByteOrder.LITTLE_ENDIAN)
	{
		if (order.Equals(ByteOrder.LITTLE_ENDIAN))
		{
			return BitConverter.GetBytes(value);
		}
		return new byte[4]
		{
			(byte)((uint)(value >> 24) & 0xFFu),
			(byte)((uint)(value >> 16) & 0xFFu),
			(byte)((uint)(value >> 8) & 0xFFu),
			(byte)((uint)value & 0xFFu)
		};
	}

	public static int ToInt32(byte[] value, int startIndex, ByteOrder order)
	{
		if (order.Equals(ByteOrder.LITTLE_ENDIAN))
		{
			return BitConverter.ToInt32(value, startIndex);
		}
		return (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | value[startIndex + 3];
	}

	public static void UInt32FromByteArray(uint[] destination, int destinationIndex, byte[] source, int sourceIndex, int count, ByteOrder order = ByteOrder.LITTLE_ENDIAN)
	{
		if (order.Equals(ByteOrder.LITTLE_ENDIAN))
		{
			for (int j = 0; j < count; j += 4)
			{
				destination[destinationIndex++] = (uint)(source[sourceIndex++] | (source[sourceIndex++] << 8) | (source[sourceIndex++] << 16) | (source[sourceIndex++] << 24));
			}
		}
		else
		{
			for (int i = 0; i < count; i += 4)
			{
				destination[destinationIndex++] = (uint)((source[sourceIndex++] << 24) | (source[sourceIndex++] << 16) | (source[sourceIndex++] << 8) | source[sourceIndex++]);
			}
		}
	}

	public static uint UInt32FromByteArray(byte[] source, int sourceIndex, ByteOrder order = ByteOrder.LITTLE_ENDIAN)
	{
		if (order.Equals(ByteOrder.LITTLE_ENDIAN))
		{
			return (uint)(source[sourceIndex] | (source[sourceIndex + 1] << 8) | (source[sourceIndex + 2] << 16) | (source[sourceIndex + 3] << 24));
		}
		return (uint)((source[sourceIndex] << 24) | (source[sourceIndex + 1] << 16) | (source[sourceIndex + 2] << 8) | source[sourceIndex + 3]);
	}

	public static string ToString(byte[] input, int offset, int count)
	{
		StringBuilder sb = new StringBuilder(count << 1);
		for (int i = 0; i < count; i++)
		{
			sb.Append(input[offset++].ToString("X2"));
		}
		return sb.ToString();
	}

	public static string ToString(byte[] input)
	{
		return ToString(input, 0, input.Length);
	}

	public static string ToString(byte[] input, int offset)
	{
		return ToString(input, offset, input.Length - offset);
	}
}
